package org.hardcode.rpc.provider.common.handler;

import com.alibaba.fastjson.JSONObject;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.hardcode.rpc.common.helper.RpcServiceHelper;
import org.hardcode.rpc.common.threadpool.ServerThreadPool;
import org.hardcode.rpc.protocol.RpcProtocol;
import org.hardcode.rpc.protocol.enumeration.RpcStatus;
import org.hardcode.rpc.protocol.enumeration.RpcType;
import org.hardcode.rpc.protocol.header.RpcHeader;
import org.hardcode.rpc.protocol.request.RpcRequest;


import org.hardcode.rpc.protocol.request.RpcRequest;
import org.hardcode.rpc.protocol.response.RpcResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;


/**
 * 由于本rpc框架的底层的通信依赖的Netty框架，Netty中可以通过 Handler处理器进行消息的收发，
 * 我们使用 SimpleChannelInboundHandler 类实现消息的收发功能
 */
public class RpcProviderHandler extends SimpleChannelInboundHandler<RpcProtocol<RpcRequest>> {

    private final Logger logger = LoggerFactory.getLogger(RpcProviderHandler.class);

    private final Map<String, Object> handlerMap;

    public RpcProviderHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    /**
     * 打印接收到的数据和存储在handlerMap中数据,并将接收的数据直接返回
     *
     * @param channelHandlerContext
     * @param protocol
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx,
                                RpcProtocol<RpcRequest> protocol) throws Exception {

        logger.info("RPC提供者收到的数据为----->{}", JSONObject.toJSON(protocol));
//        logger.info("handlerMap 中存放的数据如下:");
//
//        for (Map.Entry<String, Object> entry : handlerMap.entrySet()) {
//            logger.info(entry.getKey() + "-------" + entry.getValue());
//        }
//        //直接返回数据
//        //这是以前的直接返回的数据
//        //   channelHandlerContext.writeAndFlush(o.toString()+"\n");
//        //2023-10-23
//        RpcHeader header = rpcProtocol.getHeader();
//        RpcRequest request = rpcProtocol.getBody();
//        //将 header中的消息类型设置 为响应类型的消息
//        header.setMsgType((byte)RpcType.RESPONSE.getType());
//        //构建 响应协议 数据
//        RpcProtocol<RpcResponse> responseRpcProtocol= new RpcProtocol<RpcResponse>();
//
//        RpcResponse response  = new RpcResponse();
//        response.setResult("数据交互成功");
//        response.setAsync(request.getAsync());
//        response.setOneWay(request.getOneWay());
//        //setheader
//        responseRpcProtocol.setHeader(header);
//        responseRpcProtocol.setBody(response);
//        channelHandlerContext.writeAndFlush(responseRpcProtocol);

        ServerThreadPool.submit(() -> {
            RpcHeader header = protocol.getHeader();
            header.setMsgType((byte) RpcType.RESPONSE.getType());

            RpcRequest request = protocol.getBody();
            logger.debug("receive request" + header.getRequestId());

            RpcProtocol<RpcResponse> responseRpcProtocol = new RpcProtocol<RpcResponse>();

            RpcResponse response = new RpcResponse();
            // 执行某个方法
            try {
                Object result = handle(request);
                response.setResult(result);
                response.setAsync(request.getAsync());
                response.setOneWay(request.getOneWay());
                header.setStatus((byte) RpcStatus.SUCCESS.getCode());
            } catch (Throwable e) {
                response.setError(e.toString());
                header.setStatus((byte)RpcStatus.FAIL.getCode());
            }
            responseRpcProtocol.setHeader(header);
            responseRpcProtocol.setBody(response);

            ctx.writeAndFlush(responseRpcProtocol).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    logger.debug("send response for  request"+ header.getRequestId());
                }
            });


        });


    }

    /**
     *
     * @param request
     * @return
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     */
    private Object handle(RpcRequest request) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {

        String serviceKey = RpcServiceHelper.buildServiceKey(request.getClassName(), request.getVersion(), request.getGroup());
        //对象
        Object serviceBean = handlerMap.get(serviceKey);
        if (serviceBean == null) {
            throw new RuntimeException(String.format("service not exist: %s:%s", request.getClassName(), request.getMethodName()));
        }

        //哪个类
        Class<?> serviceClass = serviceBean.getClass();
        //方法名
        String methodName = request.getMethodName();
        //参数类
        Class<?>[] parameterTypes = request.getParameterTypes();
        //参数值
        Object[] parameters = request.getParameters();

        logger.debug(serviceClass.getName());
        logger.debug(methodName);
        if (parameterTypes != null && parameterTypes.length > 0) {
            for (int i = 0; i < parameterTypes.length; i++) {
                logger.debug(parameterTypes[i].getName());
            }
        }

        if (parameters != null && parameters.length > 0) {
            for (int i = 0; i < parameters.length; i++) {
                logger.debug(parameters[i].toString());
            }
        }

    return     invokeMethod(serviceBean,serviceClass,methodName,parameterTypes,parameters);

    }

    /**
     * JDK  目前使用jdk 动态代理的方式   此处埋点
     *
     * @param serviceBean
     * @param serviceClass
     * @param methodName
     * @param parameterTypes
     * @param parameters
     * @return
     */
    private Object invokeMethod(Object serviceBean, Class<?> serviceClass, String methodName, Class<?>[] parameterTypes, Object[] parameters) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {


        //通过类的class 方法名和方法的类型 查找到方法
        Method method = serviceClass.getMethod(methodName, parameterTypes);
        //java 方法不做访问检查
        method.setAccessible(true);
        //通过方法 对象 和方法的参数 调用某个方法
        Object invoke = method.invoke(serviceBean, parameters);
        return  invoke;


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext  ctx,Throwable cause){
        logger.error("server caught exception",cause);
        ctx.close();
    }


}
